/**
 * Project 67th
 */


#include "EventLoop.h"
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

/**
 * EventLoop implementation
 */


/**
 * @param acceptor
 */
EventLoop::EventLoop(Acceptor & acceptor) 
    : _epfd(createEpollFd())
    , _evtList(1024)
    , _isLooping(false)
    , _acceptor(acceptor)
      , _conns()
{
    //获取listenfd，放到红黑树上进行监听
    int listenfd = _acceptor.fd();
    addEpollFd(listenfd);

}

EventLoop::~EventLoop() {
    ::close(_epfd);
}

/**
 * @return void
 */
void EventLoop::loop() {
    _isLooping = true;
    while(_isLooping)
    {
        waitEpollFd();
    }
}

/**
 * @return void
 */
void EventLoop::unloop() {
    _isLooping = false;
}

/**
 * @return int
 */
int EventLoop::createEpollFd() {
    int fd = epoll_create(10);
    if(fd < 0)
    {
        perror("createEpollFd");
        return -1;
    }
    return fd;
}

/**
 * @param fd
 * @return void
 */
void EventLoop::addEpollFd(int fd) {
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;

    int ret = epoll_ctl(_epfd,
                        EPOLL_CTL_ADD,
                        fd,
                        &evt);
    if(ret < 0)
    {
        perror("addEpollFd");
        return;
    }
}

/**
 * @param fd
 * @return void
 */
void EventLoop::delEpollFd(int fd) {
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;

    int ret = epoll_ctl(_epfd,
                        EPOLL_CTL_DEL,
                        fd,
                        &evt);
    if(ret < 0)
    {
        perror("addEpollFd");
        return;
    }
}

/**
 * @return void
 */
void EventLoop::waitEpollFd() {
    int nready = 0;

    do{
        //回顾一下，获取vector存放元素的空间的首地址的方式
        nready = ::epoll_wait(_epfd, _evtList.data(), _evtList.size(), 5000);
    }while(-1 == nready && errno == EINTR);

    if(-1 == nready)
    {
        cerr << "-1 == nready" << endl;
        return;
    }
    else if(0 == nready)
    {
        cout << ">> epoll_wait timeout! " << endl;
    }
    else
    {
        //虽然vector的容量设置得比较大，1024
        //但是也有可能随着客户端的增多，
        //如果连接的客户端的个数超过了1024
        //那么有可能同一时间有超过1024个文件描述符就绪
        //原本的就绪列表就不够用了
        //但是epoll_wait不可能使用vector合法的方式让其扩容
        //所以只能手动扩容
        if((int)_evtList.size() == nready)
        {
            _evtList.resize(2 * nready);
        }

        for(int idx = 0; idx < nready; ++idx)
        {
            int listenfd = _acceptor.fd();
            int fd = _evtList[idx].data.fd;
            if(fd == listenfd)
            {
                //有新的连接请求过来
                handleNewConnection();
            }
            else
            {
                //老的连接有数据发过来
                handleMessage(fd);
            }
        }
    }
}

/**
 * @return void
 */
void EventLoop::handleNewConnection() {
    int connfd = _acceptor.accept();
    if(connfd < 0)
    {
        perror("handleNewConnection");
        return;
    }

    //添加到红黑树进行监听
    addEpollFd(connfd);

    //使用connfd创建出TcpConnection，并且交给shared_ptr管理
    TcpConnectionPtr con(new TcpConnection(connfd));
    con->setNewConnectionCallback(_onNewConnection);
    con->setMessageCallback(_onMessage);
    con->setCloseCallback(_onClose);

    //存放键值对
    /* _conns.insert({connfd,con}); */
    _conns[connfd] = con;

    //连接建立事件如何处理
    //////////////////////////////////
    con->handleNewConnectionCallback();
}

/**
 * @param fd
 * @return void
 */
void EventLoop::handleMessage(int fd) {
    auto it = _conns.find(fd);
    if(it != _conns.end())
    {   
        bool flag = it->second->isClosed();
        //服务端结束数据
        if(flag)
        {
            //连接断开事件如何处理
            /////////////////////////////////
            it->second->handleCloseCallback();

            //从红黑树上删除失效的连接所对应的connfd
            //以及map中删除相应的记录
            delEpollFd(fd);
            _conns.erase(it);
        }
        else
        {
            //消息到达的事件如何处理           
            /////////////////////////////////
            it->second->handleMessageCallback();
        }
    }
    else
    {
        cout << "该连接不存在" << endl;
        return;
    }
}


void EventLoop::setNewConnectionCallback(TcpConnectionCallback && cb)
{
    //如果这样写，那么还是一次赋值
    //因为有名字的右值引用其本身是左值
    //_onNewConnection = cb;
    _onNewConnection = std::move(cb);
}
void EventLoop::setMessageCallback(TcpConnectionCallback && cb)
{
    _onMessage = std::move(cb);
}
void EventLoop::setCloseCallback(TcpConnectionCallback && cb)
{
    _onClose = std::move(cb);
}
